﻿using System;
using System.Collections.Generic;
using System.Web;
using System.Net;
using System.IO;
using System.Xml;
using System.Data;

/// <summary>
/// Summary description for FlightBAL
/// </summary>
public class FlightBAL
{
    private FlightDAL _flightDAL;
    public FlightBAL()
    {
        _flightDAL = new FlightDAL();
    }

    private List<Location> convertDataTableToLocation(DataTable dt)
    {
        List<Location> locList = new List<Location>();
        if (dt != null)
        {
            foreach (DataRow row in dt.Rows)
            {
                Location loc = new Location();
                loc.ID = Convert.ToInt32(row["CityID"]);
                loc.Name = Convert.ToString(row["City Name"]);
                loc.Country = Convert.ToString(row["Country"]);
                if (row.IsNull("State"))
                {
                    loc.FullName = loc.Name + "," + loc.Country;
                }
                else
                {
                    loc.FullName = loc.Name + "," + Convert.ToString(row["State"]) + "," + loc.Country;
                }
                loc.Longitude = Convert.ToDouble(row["Longitude"]);
                loc.Latitude = Convert.ToDouble(row["Latitude"]);
                loc.GMT = Convert.ToDouble(row["GMT"]);

                locList.Add(loc);
            }
        }
        return locList;
    }

    private List<Flight> convertDataTableToFlight(DataTable dt)
    {
        List<Flight> flightList = new List<Flight>();

        if (dt != null)
        {
            foreach (DataRow row in dt.Rows)
            {
                Flight _flight = new Flight();
                _flight.FlightID = Convert.ToInt32(row["FlightID"]);
                _flight.Smoking = Convert.ToBoolean(row["isAllowSmoking"]);
                _flight.SeniorDiscount = Convert.ToInt32(row["Senior Discount"]);
                _flight.Plane.PlaneID = Convert.ToInt32(row["AirPlaneID"]);
                _flight.Plane.Name = Convert.ToString(row["Plane Name"]);
                if (dt.Columns.Contains("Total Bussiness Class Seats"))
                {
                    _flight.Plane.TotalBussSeat = Convert.ToInt32(row["Total Bussiness Class Seats"]);
                    _flight.Plane.TotalFirstSeat = Convert.ToInt32(row["Total First Class Seats"]);
                    _flight.Plane.TotalClubSeat = Convert.ToInt32(row["Total Club Class Seats"]);
                }
                _flight.FlyHour = Convert.ToDouble(row["FlyHour"]);
                if (dt.Columns.Contains("FlightStatus"))
                {
                    _flight.FlightStatus = Convert.ToString(row["FlightStatus"]); 
                }
                _flight.FirstPrice = Convert.ToInt32(row["Fisrt Class Price"]);
                _flight.DepartTime = Convert.ToDateTime(row["Depart Time"]);
                _flight.ClubPrice = Convert.ToInt32(row["Club Price"]);
                _flight.ChildDiscount = Convert.ToInt32(row["Child Discount"]);
                _flight.BussPrice = Convert.ToInt32(row["Bussiness Class Price"]);
                _flight.AvailFirstSeat = Convert.ToInt32(row["Avail First Seat"]);
                _flight.AvailClubSeat = Convert.ToInt32(row["Avail Club Seat"]);
                _flight.AvailBussSeat = Convert.ToInt32(row["Avail Bussiness Seat"]);
                _flight.Route.Destination.ID = Convert.ToInt32(row["DestinationCityID"]);
                _flight.Route.Origin.ID = Convert.ToInt32(row["OriginalCityID"]);

                flightList.Add(_flight);
            }
        }
        return flightList;
    }

    #region Step 1. Search for Locations

    #region Check whether place is known in Database or not
    public bool PlaceIsKnown(Location location)
    {

        bool OK;
        try
        {
            StreamReader sReader = File.OpenText(HttpContext.Current.Server.MapPath("~/CityList/CityList.txt"));
            String text = sReader.ReadToEnd();
            if (text.ToLower().Contains(location.FullName.ToLower()))
            {
                OK = true;
            }
            else
            {
                OK = false;
            }
        }
        catch (Exception e)
        {
            throw e;
        }
        return OK;
    }
    #endregion

    public List<Location> SearchPlacefromWebServices(string place, string country)
    {
        
        List<Location> _locationList = new List<Location>();

        string _dataString;
        string AppID = "8Gcdc3zV34HyezwKuaquQeRQ.1UcBsH6nC7.mpZYKVqvNMo4ioWHfYWAtLS9sqTNEhjRY2thpA--";
        #region get Data String from Yahoo! Place Finder
        string urlstring = "http://where.yahooapis.com/geocode?key=" + AppID + "&city=" + place + "&country=" + country;
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(urlstring);

        IWebProxy proxy = WebRequest.GetSystemWebProxy();
        proxy.Credentials = CredentialCache.DefaultCredentials;

        request.Proxy = proxy;
        //request.Method = "GET";
        //request.UserAgent = "Mozilla/4.0+(compatible;+MSIE+6.0;+Windows+NT+5.1;+.NET+CLR+1.1.4322)";
        //request.Timeout = -1;


        WebResponse response = request.GetResponse();
        Stream stream = response.GetResponseStream();
        StreamReader reader = new StreamReader(stream);
        _dataString = reader.ReadToEnd();
        reader.Close();
        stream.Close();
        response.Close();
        #endregion

        XmlReader _xmlreader;
        #region from _datastring to xml
        XmlReaderSettings setting = new XmlReaderSettings();
        setting.IgnoreComments = true;
        setting.IgnoreWhitespace = true;

        _xmlreader = XmlReader.Create(new StringReader(_dataString), setting);
        #endregion

        #region Check status
        if (_xmlreader.ReadToFollowing("Error"))
        {
            if (_xmlreader.ReadElementContentAsInt() != 0)
            {
                return _locationList;
            }
        }
        else
        {
            return _locationList;
        }
        #endregion

        Location _location;

        #region Read Data
        _xmlreader.ReadToFollowing("Found");
        int found = _xmlreader.ReadElementContentAsInt();
        if (found == 0)
        {
            return _locationList;
        }
        else
        {
            for (int i = 0; i < found; i++)
            {
                _location = new Location();
                _xmlreader.ReadToFollowing("latitude");
                _location.Latitude = _xmlreader.ReadElementContentAsDouble();
                _location.Longitude = _xmlreader.ReadElementContentAsDouble();

                _xmlreader.ReadToFollowing("city");
                _location.Name = _xmlreader.ReadElementContentAsString();
                _location.FullName += _location.Name;

                _xmlreader.ReadToFollowing("state");
                if (!_xmlreader.IsEmptyElement)
                {
                    _location.FullName += ("," + _xmlreader.ReadElementContentAsString());
                    _location.Country = _xmlreader.ReadElementContentAsString();
                }
                else
                {
                    _xmlreader.ReadToFollowing("country");
                    _location.Country = _xmlreader.ReadElementContentAsString();
                }

                _location.FullName += ("," + _location.Country);

                _locationList.Add(_location);
            }
        }

        #endregion

        #region using google geocode

        //string _temp1 = "";
        //string _temp2 = "";
        //string _name = "";
        //string _country = "";
        //string _fullName = "";
        //string _type = "";
        //double _lat = 0;
        //double _lng = 0;
        //while (_xmlreader.Read())
        //{
        //    if (_xmlreader.NodeType == XmlNodeType.Element)
        //    {
        //        #region FullName
        //        if (_xmlreader.Name == "formatted_address")
        //        {
        //            _fullName = _xmlreader.ReadElementContentAsString();
        //        }
        //        #endregion
        //        #region Location & Country
        //        if (_xmlreader.Name == "address_component")
        //        {
        //            _xmlreader.ReadToDescendant("long_name");

        //            _temp1 = _xmlreader.ReadElementContentAsString();

        //            _xmlreader.ReadToFollowing("type");

        //            _temp2 = _xmlreader.ReadElementContentAsString();

        //            if (_temp2 == "locality")
        //            {
        //                _name = _temp1;
        //                _type = _temp2;
        //            }
        //            else if (_temp2 == "country")
        //            {
        //                _country = _temp1;
        //            }

        //        }
        //        #endregion
        //        #region longitude & latitude
        //        else if (_xmlreader.Name == "location")
        //        {
        //            _xmlreader.ReadToDescendant("lat");
        //            _lat = _xmlreader.ReadElementContentAsDouble();
        //            _lng = _xmlreader.ReadElementContentAsDouble();

        //        }
        //        #endregion
        //    }
        //    #region save list
        //    else if (_xmlreader.NodeType == XmlNodeType.EndElement && _xmlreader.Name == "result")
        //    {
        //        if (_type == "locality")
        //        {
        //            bool isNew = true;
        //            foreach (Location _loc in _locationList)
        //            {
        //                if (_loc.Name == _name)
        //                {
        //                    isNew = false;
        //                }
        //            }

        //            if (isNew)
        //            {
        //                _location = new Location();
        //                _location.FullName = _fullName;
        //                _location.Name = _name;
        //                _location.Country = _country;
        //                _location.Latitude = _lat;
        //                _location.Longitude = _lng;


        //                _locationList.Add(_location); 
        //            }
        //        }

        //    }
        //    #endregion
        //} 
        #endregion

        return _locationList;
    }

    public List<Location> GetNearestCities(Location _loc)
    {
        DataSet ds = _flightDAL.GetNearestLocation(_loc.Longitude, _loc.Latitude, _loc.Country);
        DataTable dt = ds.Tables[0];
        List<Location> locationList = convertDataTableToLocation(dt);
        return locationList;
    }

    public Location GetLocationByID(int ID)
    {
        DataSet ds = _flightDAL.GetLocationByID(ID);
        DataTable dt = ds.Tables[0];
        List<Location> locationList = convertDataTableToLocation(dt);
        return locationList[0];
    }

    public Location GetLocationByName(string name, string country)
    {
        DataSet ds = _flightDAL.GetLocationByName(name, country);
        DataTable dt = ds.Tables[0];
        List<Location> locationList = convertDataTableToLocation(dt);
        return locationList[0];
    }

    #region Calculate Distance
    public double calDistance(Location loc1, Location loc2) //Distance by km
    {
        double theta = loc1.Longitude - loc2.Longitude;
        double dist = Math.Sin(deg2rad(loc1.Latitude)) * Math.Sin(deg2rad(loc2.Latitude)) + Math.Cos(deg2rad(loc1.Latitude)) * Math.Cos(deg2rad(loc2.Latitude)) * Math.Cos(deg2rad(theta));
        dist = Math.Acos(dist);
        dist = rad2deg(dist);
        dist = dist * 60 * 1.1515 * 1.609344;
        return Math.Round(dist, 1);
    }

    private double deg2rad(double deg)
    {
        return (deg * Math.PI / 180.0);
    }

    private double rad2deg(double rad)
    {
        return (rad / Math.PI * 180.0);
    }
    #endregion

    #endregion

    #region Step 2. Search for Route
    public List<BigRoute> SearchRoute(Location origin, Location destiantion, List<DateTime> SellectedDates)
    {
        List<BigRoute> routeList = new List<BigRoute>();

        if (origin.ID == destiantion.ID)
        {
            return routeList;
        }
        string DayofWeek = "";
        if (SellectedDates != null)
        {
            foreach (DateTime date in SellectedDates)
            {
                string a = date.DayOfWeek.ToString().Remove(3);
                if (!DayofWeek.Contains(a))
                {
                    DayofWeek += (a + ",");
                }
            }
        }
        else
        {
            DayofWeek = "*";
        }

        List<object> outputobj = _flightDAL.GetRoute(origin, destiantion, DayofWeek);
        System.Data.SqlClient.SqlParameterCollection _para = outputobj[0] as System.Data.SqlClient.SqlParameterCollection;
        DataSet ds = outputobj[1] as DataSet;

        string status = _para["@status"].Value as string;
        if (status == "direct")
        #region Direct Flight
        {
            foreach (DataRow row in ds.Tables[0].Rows)
            {
                SmallRoute route = new SmallRoute();
                route.RouteID = Convert.ToInt32(row["RouteID"]);
                route.Origin = GetLocationByID(Convert.ToInt32(row["OriginalCityID"]));
                route.Destination = GetLocationByID(Convert.ToInt32(row["DestinationCityID"]));
                route.FlyHour = Convert.ToInt32(row["FlyHour"]);

                BigRoute bigroute = new BigRoute();
                bigroute.RouteList.Add(route);

                routeList.Add(bigroute);
            }
            return routeList;
        }
        #endregion
        else if (status == "1 stop")
        #region 1 Stops
        {
            foreach (DataRow row in ds.Tables[0].Rows)
            {
                SmallRoute route1 = new SmallRoute();
                SmallRoute route2 = new SmallRoute();

                route1.RouteID = Convert.ToInt32(row["RouteID1"]);
                route1.Origin = GetLocationByID(Convert.ToInt32(row["OriginalCityID"]));
                route1.Destination = GetLocationByID(Convert.ToInt32(row["Stop"]));
                route1.FlyHour = Convert.ToInt32(row["FlyHour1"]);

                route2.RouteID = Convert.ToInt32(row["RouteID2"]);
                route2.Origin = GetLocationByID(Convert.ToInt32(row["Stop"]));
                route2.Destination = GetLocationByID(Convert.ToInt32(row["DestinationCityID"]));
                route2.FlyHour = Convert.ToInt32(row["FlyHour2"]);

                BigRoute bigroute = new BigRoute();
                bigroute.RouteList.Add(route1);
                bigroute.RouteList.Add(route2);

                routeList.Add(bigroute);
            }
            return routeList;
        }
        #endregion
        else
        {
            List<SmallRoute> allroute = new List<SmallRoute>();
            foreach (DataRow row in ds.Tables[0].Rows)
            {
                SmallRoute route = new SmallRoute();
                route.RouteID = Convert.ToInt32(row["RouteID"]);
                route.Origin.ID = Convert.ToInt32(row["OriginalCityID"]);
                route.Destination.ID = Convert.ToInt32(row["DestinationCityID"]);
                route.FlyHour = Convert.ToInt32(row["FlyHour"]);

                allroute.Add(route);
            }
            int OriginID = origin.ID;
            int DestinationID = destiantion.ID;
            int stopNumber = 0;
            List<BigRoute> output = new List<BigRoute>();
            output = SearchRouteWithManyStop(OriginID, DestinationID, allroute, stopNumber, output);
            if (output != null)
            {
                foreach (BigRoute bigroute in output)
                {
                    foreach (SmallRoute smallroute in bigroute.RouteList)
                    {
                        smallroute.Origin = GetLocationByID(smallroute.Origin.ID);
                        smallroute.Destination = GetLocationByID(smallroute.Destination.ID);
                    }

                }
                return output;
            }
            else
            {
                return new List<BigRoute>();
            }
        }
    }

    private List<BigRoute> SearchRouteWithManyStop(int OriginID, int DestinationID, List<SmallRoute> RouteList, int stopNumber, List<BigRoute> output)
    {
        stopNumber++;

        #region Remove route that too long
        for (int i = 0; i < output.Count; i++)
        {
            BigRoute bigroute = output[i];
            if (bigroute.RouteList.Count > 6)
            {
                output.Remove(bigroute);
                i--;
            }
        }
        #endregion


        bool Found = false;
        int rountNum = 1000;
        #region If found a possible route then remove all unfinish route that longer than possible route.
        foreach (BigRoute bigroute in output)
        {
            if (bigroute.RouteList[bigroute.RouteList.Count - 1].Destination.ID == DestinationID)
            {
                Found = true;
                if (rountNum > bigroute.RouteList.Count)
                {
                    rountNum = bigroute.RouteList.Count;
                }
            }
        }

        if (Found)
        {
            for (int i = 0; i < output.Count; i++)
            {
                if (output[i].RouteList[output[i].RouteList.Count - 1].Destination.ID != DestinationID && output[i].RouteList.Count >= rountNum)
                {
                    output.RemoveAt(i);
                    i--;
                }
            }
        }
        #endregion

        if (stopNumber > 1 && RouteList.Count == 0)
        #region No more Route >> return
        {
            if (Found)
            {
                for (int i = 0; i < output.Count; i++)
                {
                    if (output[i].RouteList[output[i].RouteList.Count - 1].Destination.ID != DestinationID)
                    {
                        output.RemoveAt(i);
                        i--;
                    }
                }
                return output;
            }
            else
            {
                return null;
            }
        }
        #endregion

        List<SmallRoute> temp = new List<SmallRoute>();

        #region Check exists route to Destination
        foreach (SmallRoute route in RouteList)
        {
            if (route.Destination.ID == DestinationID)
            {
                temp.Add(route);
            }
        }

        if (temp.Count == 0)
        {
            return null;
        }
        #endregion
        temp.Clear();

        List<SmallRoute> nextStop = new List<SmallRoute>();

        #region Get Next posible stop
        foreach (SmallRoute route in RouteList)
        {
            if (route.Origin.ID == OriginID)
            {
                nextStop.Add(route);
            }
        }
        #endregion

        #region Remove pasted Place
        for (int i = 0; i < RouteList.Count; i++)
        {
            if ((RouteList[i].Origin.ID == OriginID || RouteList[i].Destination.ID == OriginID) && (RouteList[i].Destination.ID != DestinationID))
            {
                RouteList.RemoveAt(i);
                i--;
            }
        }
        #endregion

        if (nextStop.Count == 0)
        #region Remove DeadEnd ( nextStop.Count == 0 mean No More Route )
        {
            for (int i = 0; i < output.Count; i++)
            {
                int endpoint = output[i].RouteList.Count - 1;
                if (output[i].RouteList[endpoint].Destination.ID == OriginID)
                {
                    output.RemoveAt(i);
                    i--;
                }
            }
        }
        #endregion
        else
        #region Add New Route Or Extend Old Route
        {
            if (output.Count == 0)
            {
                foreach (SmallRoute smallroute in nextStop)
                {
                    BigRoute newRoute = new BigRoute();
                    newRoute.RouteList.Add(smallroute);
                    output.Add(newRoute);
                }
            }
            else
            {
                for (int i = 0; i < output.Count; i++)
                {
                    BigRoute oldRoute = output[i];
                    if (oldRoute.RouteList[oldRoute.RouteList.Count - 1].Destination.ID == OriginID)
                    {
                        output.Remove(oldRoute);
                        foreach (SmallRoute smallroute in nextStop)
                        {
                            BigRoute newRoute = new BigRoute();
                            newRoute.RouteList.AddRange(oldRoute.RouteList);
                            newRoute.RouteList.Add(smallroute);
                            output.Add(newRoute);
                        }
                    }
                }
            }
        }
        #endregion

        List<Int32> ListofCurrentstop = new List<int>();
        #region get Current stop
        foreach (BigRoute bigroute in output)
        {
            int currentstop = bigroute.RouteList[bigroute.RouteList.Count - 1].Destination.ID;
            if (!ListofCurrentstop.Contains(currentstop))
            {
                ListofCurrentstop.Add(currentstop);
            }
        }
        #endregion

        foreach (int currentstop in ListofCurrentstop)
        {
            if (currentstop != DestinationID)
            {
                output = SearchRouteWithManyStop(currentstop, DestinationID, RouteList, stopNumber, output);
                if (output == null)
                {
                    break;
                } 
            }
        }

        return output;
    }
    #endregion

    #region Step 3. Search for Flight
    public List<Flight> SearchFirstFlight(BigRoute _route, DateTime _departtime, int _NumberofPassenger)
    {
        List<Flight> FlightList = new List<Flight>();
        DataSet ds = _flightDAL.SearchFirstFlight(_route.RouteList[0].RouteID, _departtime, _NumberofPassenger);
        FlightList = convertDataTableToFlight(ds.Tables[0]);
        foreach (Flight _flight in FlightList)
        {
            _flight.Route = _route.RouteList[0];
        }
        return FlightList;
    }

    public List<Flight> SearchNextFlights(Flight _firstFlight, BigRoute _bigRoute, int NumberofPassenger)
    {
        #region get Depart time (with GMT)
        DateTime _departTime = _firstFlight.DepartTime.AddHours(_firstFlight.FlyHour);
        _departTime = _departTime.AddHours((-1) * _firstFlight.Route.Origin.GMT);
        _departTime = _departTime.AddHours(_firstFlight.Route.Destination.GMT); 
        #endregion
        List<Flight> _flightList = new List<Flight>();
        _flightList.Add(_firstFlight);
        for (int i = 1; i < _bigRoute.RouteList.Count; i++)
        {
            DataSet ds = _flightDAL.SearchNextFlights(_bigRoute.RouteList[i].RouteID, _departTime, NumberofPassenger);
            if (ds == null)
            {
                return null;
            }
            List<Flight> tempList = convertDataTableToFlight(ds.Tables[0]);
            if (tempList.Count != 1)
            {
                return null;
            }
            Flight _nextFlight = tempList[0];
            _nextFlight.Route = _bigRoute.RouteList[i];
            _flightList.Add(_nextFlight);
            #region get next Depart time (with GMT)
            _departTime = _nextFlight.DepartTime.AddHours(_nextFlight.FlyHour);
            _departTime = _departTime.AddHours((-1) * _nextFlight.Route.Origin.GMT);
            _departTime = _departTime.AddHours(_nextFlight.Route.Destination.GMT); 
            #endregion
        }
        return _flightList;
    } 
    #endregion

    public Flight getFlightbyID(int FlightID)
    {
        DataSet ds = _flightDAL.getFlightbyID(FlightID);

        Flight flight = convertDataTableToFlight(ds.Tables[0])[0];

        flight.Route.Origin = GetLocationByID(flight.Route.Origin.ID);
        flight.Route.Destination = GetLocationByID(flight.Route.Destination.ID);

        return flight;
    }

    public List<Flight> findSimilarFlight(Ticket ticket, int numofMonth)
    {
        int originid = ticket.Flight.Route.Origin.ID;
        int destinationid = ticket.Flight.Route.Destination.ID;
        string seatclass = ticket.TicketDetail[0].SeatClass;
        int numofPass = ticket.TicketDetail.Count;

        DataSet ds = _flightDAL.SearchSimilarFlight(originid, destinationid, numofPass, numofMonth);

        List<Flight> fList= convertDataTableToFlight(ds.Tables[0]);
        return fList;
    }

    public void CancelFlight(int flightID)
    {
        _flightDAL.CancelFlight(flightID);
    }

}
